package com.dongyimai.oauth.service.impl;

import com.dongyimai.oauth.service.AuthService;
import com.dongyimai.oauth.util.AuthToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Base64;
import java.util.Map;

/**
 * @Author: zola
 * @Date: 2021/9/19 - 09 - 19 - 23:06
 * @Description: com.dongyimai.oauth.service.impl
 * @Version: 1.0
 */
@Service
public class AuthServiceImpl implements AuthService {

    // 基于Ribbon的远程调用:则注意RestTemplate对象根据controller路径来实现远程调用
    /*
    * 1.如何获取路径：在eureka实现远程调用的时候，要么写死路径，要么通过负载均衡策略获取路径
    * (可以理解为在基于LoadBalance(负载均衡)的服务调用的基础上，使用实现负载均衡的客户端对象来获取提供者中在eureka中注册服务的路径)
    * 2.如何远程调用：通过RestTemplate所封装的根据路径来实现该路径上的方法(请求)的远程调用
    * */
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;
    
    /**
     * @param: 用户名，密码，clientId,secret
     * @return: 获取令牌
     * @description: 这是一种通过远程调用http://localhost:9999/oauth/token上的路径 生成token的方法 是个basic token
     * @analyze: 并将生成的token进行加密封装，只要用户登录就将生成的basic token 放入session中 其实是 认证中心第三方登录的basic token
     */
    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        // 1.1 获取远程调用的路径
        ServiceInstance instance = loadBalancerClient.choose("USER-AUTH");// 实现负载均衡的客户端对象.choose("在eureka注册中心注册的提供者服务名")
        // 给对象可以理解为注册到注册中心提供服务的对象——提供者客户端对象
        if (instance == null) {
            throw new RuntimeException("您访问的服务不存在:"+ HttpStatus.NOT_FOUND);
        }
        // 1.2 获取取路径=在注册中心注册的路径(http://localhost:9999)+controller请求路径("/oauth/token")
        String url = instance.getUri().toString() + "/oauth/token";

        // 2.2 定义请求体
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap();
        requestBody.add("grant_type","password"); // 指定采用密码授权模式
        requestBody.add("username",username); // 指定UserDetails配置类中的用户名
        requestBody.add("password",password); // 采用密码模式指定好的密码，目前适用固定，后期改动

        // 2.3 定义请求头
        MultiValueMap<String, String> requestHeader = new LinkedMultiValueMap();
        // clientId:clientSecret =>JWT中HS256加密算法进行加密：Base64.getEncoder().encode()
        String str = clientId + ":" + clientSecret;
        byte[] encode = Base64.getEncoder().encode(str.getBytes());
        requestHeader.add("Authorization","Basic "+new String(encode));

        // 2.1 实现远程调用：结果产生access_token,refresh_token,jti
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(requestBody, requestHeader), Map.class);
        Map map = responseEntity.getBody();
        if (map == null || map.get("access_token") == null || map.get("refresh_token") == null || map.get("jti") == null){
            throw new RuntimeException("生成token令牌失败");
        }

        // 2.4 将获取的结果用AuthToken实体类进行封装
        // AuthToken(String accessToken, String refreshToken, String jti)
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String)map.get("access_token"));  // token
        authToken.setRefreshToken(((String) map.get("refresh_token"))); // 刷新token
        authToken.setJti(((String) map.get("jti"))); // JWT令牌唯一身份标识id
        return authToken;
    }
}
